package com.example.ch03xyztwocom.filter;


import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.ch03xyztwocom.config.UserContext;
import com.example.ch03xyztwocom.enums.ResultCode;
import com.example.ch03xyztwocom.pojo.SysUser;
import com.example.ch03xyztwocom.utils.AESTool;
import com.example.ch03xyztwocom.utils.HttpClientUtil;
import com.example.ch03xyztwocom.utils.JwtUtil;
import com.example.ch03xyztwocom.vo.ResultJson;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: ws
 * @Date: 2019/4/25
 */
@Slf4j
@Order(2)
@WebFilter("/*")
public class JwtAuthenticationTokenFilter implements Filter {

    //Basic 头部
    public static final String JWT_HEAD = "Bearer ";
    //http 请求头
    public static final String AUTH_HEADER = "Authorization";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        auth(servletRequest, filterChain, httpServletResponse, request, null);
    }

    /**
     * 认证
     * @param servletRequest
     * @param filterChain
     * @param httpServletResponse
     * @param request
     * @param newToken 存在的token
     * @throws IOException
     * @throws ServletException
     */
    private void auth(ServletRequest servletRequest, FilterChain filterChain, HttpServletResponse httpServletResponse, HttpServletRequest request, String newToken) throws IOException, ServletException {

        //部分资源，，无需认证，直接放行
        if(!"XMLHttpRequest".equals(request.getHeader("x-requested-with"))){
            //非ajax 请求直接放行
            filterChain.doFilter(servletRequest, httpServletResponse);
            return;
        }
        /*String path = request.getRequestURI().substring(request.getContextPath().length());
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        if(("/pages/index.html".equals(path) && StringUtils.isNotBlank(request.getParameter("token"))) ||
                antPathMatcher.match(  "/static/**", path) ){
            filterChain.doFilter(servletRequest, httpServletResponse);
            return;
        }*/


        String token = StringUtils.isNotBlank(newToken) ? newToken : request.getHeader(AUTH_HEADER);
        if (StringUtils.isBlank(token) || !token.startsWith(JWT_HEAD)) {
            goSSOLoginPage(request, httpServletResponse);
            return;
        }

        token = token.substring(JWT_HEAD.length());
        DecodedJWT decodedJWT = null;
        try {

            decodedJWT = JwtUtil.verifyToken(JwtUtil.hs256(null), token);
            if (decodedJWT.getSubject() == null) {
                goSSOLoginPage(request, httpServletResponse);
                return;
            }

            log.warn("-----------tokenId-----------:{}", decodedJWT.getSubject());
            UserContext.setUser(new SysUser(decodedJWT.getClaim("username").asString(), null));
            filterChain.doFilter(servletRequest, httpServletResponse);

        } catch (TokenExpiredException e) {
            log.error("token过期异常", e);
//            goSSOLoginPage(request, httpServletResponse);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token过期异常");

            /**
             * token 续签: 利用
             *
             */
            JSONObject jsonObject = new JSONObject();
            String encodeStr = JwtUtil.getDecodedJWT(token).getClaim("refreshToken").asString();
            String secret = "77A7A1D6-STST-40CC-U787-49EC3576XXXX";
            AESTool aesTool = new AESTool(secret);

            try {
                String decrypt = aesTool.decrypt(encodeStr);
                jsonObject.put("refreshToken", decrypt);
            } catch (Exception ex) {
                throw new RuntimeException(ex);

            }

            String res = HttpClientUtil.doPost("http://www.sso.com:8080/refreshToken", jsonObject);
            ResultJson resultJson = JSONObject.parseObject(res, ResultJson.class);
            if(resultJson.getCode() != 200){
                goSSOLoginPage(request, httpServletResponse);
                return;
            }

            Object getToken = resultJson.getData();
//            ResultJson<Object> objectResultJson = new ResultJson<>("已经刷新token,请替换token", ResultCode.REPLACE_TOKEN, newToken);
            httpServletResponse.setHeader("refresh-token", getToken.toString());
            auth(servletRequest, filterChain, httpServletResponse, request, JWT_HEAD + getToken.toString());
            //handleResponse(httpServletResponse, objectResultJson);



        } catch (
                AlgorithmMismatchException e) {
            log.error("算法不正确异常", e);
            goSSOLoginPage(request, httpServletResponse);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                InvalidClaimException e) {
            log.error("无效的claim异常", e);
            goSSOLoginPage(request, httpServletResponse);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                SignatureVerificationException e) {
            log.error("签名验证的异常", e);
            goSSOLoginPage(request, httpServletResponse);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                JWTDecodeException e) {
            log.error("编码不正确的异常", e);
            goSSOLoginPage(request, httpServletResponse);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (JWTVerificationException e) {
            log.error("token无效异常", e);
            goSSOLoginPage(request, httpServletResponse);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        }
    }

    /**
     * 响应客户端
     *
     * @param httpServletResponse
     * @param resultJson          结果
     * @throws IOException
     */
    private void handleResponse(HttpServletResponse httpServletResponse, ResultJson resultJson) throws IOException {

//        cors(httpServletResponse);
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ObjectMapper objectMapper = new ObjectMapper();
        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(resultJson));
        httpServletResponse.getWriter().flush();
        httpServletResponse.getWriter().close();
    }

    /***
     * 跨域解决
     * @param httpServletResponse
     */
    private void cors(HttpServletResponse httpServletResponse) {

        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST,PUT,GET,OPTIONS,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,Authorization,Token");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");


    }



    private void goSSOLoginPage(HttpServletRequest request, HttpServletResponse httpServletResponse) {


        String goToUrl = "http://www.sso.com:8080/login?returnUrl=" + "http://xyz.two.com:8082/pages/login.html";

        /**
         * ajax 请求，服务端没有办法重定向
         */
//        Enumeration<String> headerNames = request.getHeaderNames();
//        if("XMLHttpRequest".equals(request.getHeader("X-Request-With"))){
        if("XMLHttpRequest".equals(request.getHeader("x-requested-with"))){

            httpServletResponse.setContentType("application/json;charset=utf-8");
            ResultJson resultJson = ResultJson.failure(ResultCode.LOGIN_FAILURE);
            resultJson.setData(goToUrl);
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                httpServletResponse.getWriter().write(objectMapper.writeValueAsString(resultJson));
                httpServletResponse.getWriter().flush();
                httpServletResponse.getWriter().close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            return ;
        }

        /**
         * 其他请求重定向
         */
        try {
            httpServletResponse.sendRedirect(goToUrl);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void destroy() {

    }

}
